跳到主要内容

3.7-跨窗口交互

Create by fall on — — 2020 Recently revised in 16 Jan 2023

窗口的交互

实现浏览器两个窗口的交互

后端方案

Web Socket,一种在单个 TCP 连接上进行全双工通讯的协议。

事件 + 客户端存储

其中 hook 可以为 SetTimeout,加上客户端存储,或者是全局事件,visibilitychange(可视更改)等事件钩子

客户端存储:cookie、localStorage、sessionStorage、indexDB

storage + event

通过事件监听

const message = {
name:"Fall"
}
localStorage.setItem('message',JSON.stringify(message))
const storageWatch = (ev)=>{
console.log(ev.key, ev.newValue, ev.oldValue) // 还有两个属性,
// storageArea:被操作的 storage 对象
// url:key发生改变的对象所在文档的 URL 地址
}
addEventListener('storage',storageWatch)

postMessage

postMessage 需要拿到 window 对象,也注定他使用的限制, 两个窗体必须建立起联系。必须保证同源

建立联系的方式

  • window.open
  • window.opener
  • iframe
// 子组件,通过获取父组件 window 以发出信息
window.addEventListener("load", iframeLoaded, false);
function iframeLoaded() {
window.parent.postMessage('Hello from the main page!', '*');
console.log('我发布了信息');
}
// 父组件,监听 message 事件,
addEventListener('message', handleMessage, false);
function handleMessage(e) {
console.log(e, e.data)
}

父传子也类似,通过 iframe.contentWindow 获取子元素 window 对象,使用该 window.postMessage 进行通信

Broadcast Channel

给多窗口用的,Service Woker 也可以使用。遵循同源策略

// Page1
var channel = new BroadcastChannel("channel-BroadcastChannel"); // 确保两个页面的频道名称一致
channel.postMessage('Hello, BroadcastChannel!')
// Page2
var channel = new BroadcastChannel("channel-BroadcastChannel");
channel.addEventListener("message", function(ev) {
console.log(ev.data)
});
// 处理事件也可以写为:
channel.onmessage=function(ev) {
console.log(ev.data)
// do something
}

SharedWorker

学习完 Web Worker 后进行补充

这是 Web Worker 之后出来的共享的 Worker,多线程解决方案,可以在不通页面 tab 之间共享这个 Worker,从而实现跨页面通信。

let  worker = new SharedWorker('/shared-worker.js', 'tabWorker');
// 接收事件
worker.port.onmessage = function (event) {
console.log('接收到 event', event);
// do something
};
// 发送事件
worker.port.postMessage(data);

shared worker 需要额外定义

// shared-worker.js
const connections = [];
// 有新的连接会触发该事件
onconnect = function (event) {
var port = event.ports[0];
connections.push(port);

port.onmessage = function (event) {
// 接收到消息时,向所有连接发送该消息
connections.forEach(function (conn) {
if (conn !== port) {
conn.postMessage(event.data);
}
});
};
// 启用监听
port.start();
};

MessageChannel

MessageChannel 接口允许我们创建一个新的消息通道,并通过它的两个 MessagePort 属性发送数据。需要先通过 postMessage 先建立联系。

此特性在 Web Worker 中可用

let { port1: receivePort, port2: sendPort } = new MessageChannel();
// 返回一个具有 port1、port2 的对象,可以通过两个对象进行传输

var ifr = document.querySelector('iframe');
var otherWindow = ifr.contentWindow;
ifr.addEventListener("load", iframeLoaded, false);
function iframeLoaded() {
otherWindow.postMessage('Hello from the main page!', '*', [sendPort]);
console.log('发送成功');
}
receivePort.onmessage = handleMessage;
function handleMessage(e) {
console.log(e.data)
}

localStorage & sessionStorage

实现方法,基于 localStorage 变化时触发的事件,window.addEventListener('storage', *function*(*event*) {}) 事件实现了 localStore 变化时候的数据监听;

参考文章

作者链接
云的世界https://juejin.cn/post/7002012595200720927
MDNhttps://developer.mozilla.org/zh-CN/docs/Web/API/MessageChannel
Coco浏览器跨 Tab 窗口通信原理及应用实践